<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    <meta name="keywords" content="Hexo Theme Keep">
    <meta name="description" content="Hexo Theme Keep">
    <meta name="author" content="aaton">
    
    <title>
        
            闭包 |
        
        aaton&#39;s study notes
    </title>
    
<link rel="stylesheet" href="/css/style.css">

    <link rel="shortcut icon" href="/images/logo.svg">
    
<link rel="stylesheet" href="/font/css/fontawesome.min.css">

    
<link rel="stylesheet" href="/font/css/regular.min.css">

    
<link rel="stylesheet" href="/font/css/solid.min.css">

    
<link rel="stylesheet" href="/font/css/brands.min.css">

    <script id="hexo-configurations">
    let KEEP = window.KEEP || {}
    KEEP.hexo_config = {"hostname":"example.com","root":"/","language":"en"}
    KEEP.theme_config = {"toc":{"enable":false,"number":false,"expand_all":false,"init_open":false},"style":{"primary_color":"#0066cc","logo":"/images/logo.svg","favicon":"/images/logo.svg","avatar":"/images/avatar.svg","font_size":null,"font_family":null,"hover":{"shadow":false,"scale":false},"first_screen":{"enable":true,"header_transparent":true,"background_img":"/images/bg.svg","description":"Keep writing and Keep loving.","font_color":null,"hitokoto":true},"scroll":{"progress_bar":false,"percent":false}},"local_search":{"enable":false,"preload":false},"code_copy":{},"code_block":{"tools":{"enable":true,"style":"default"},"highlight_theme":"default"},"side_tools":{},"pjax":{"enable":false},"lazyload":{"enable":false},"comment":{"enable":false,"use":"valine","valine":{"appid":null,"appkey":null,"server_urls":null,"placeholder":null},"gitalk":{"github_id":null,"github_admins":null,"repository":null,"client_id":null,"client_secret":null,"proxy":null},"twikoo":{"env_id":null,"region":null,"version":"1.6.8"},"waline":{"server_url":null,"reaction":false,"version":2}},"post":{"author_label":{"enable":true,"auto":true,"custom_label_list":["Trainee","Engineer","Architect"]},"word_count":{"enable":true,"wordcount":true,"min2read":true},"img_align":"left","copyright_info":false},"version":"3.6.1"}
    KEEP.language_ago = {"second":"%s seconds ago","minute":"%s minutes ago","hour":"%s hours ago","day":"%s days ago","week":"%s weeks ago","month":"%s months ago","year":"%s years ago"}
    KEEP.language_code_block = {"copy":"Copy code","copied":"Copied","fold":"Fold code block","folded":"Folded"}
    KEEP.language_copy_copyright = {"copy":"Copy copyright info","copied":"Copied","title":"Original article title","author":"Original article author","link":"Original article link"}
  </script>
<meta name="generator" content="Hexo 6.3.0"></head>


<body>
<div class="progress-bar-container">
    

    
</div>


<main class="page-container">

    

    <div class="page-main-content">

        <div class="page-main-content-top">
            
<header class="header-wrapper">

    <div class="header-content">
        <div class="left">
            
                <a class="logo-image" href="/">
                    <img src="/images/logo.svg">
                </a>
            
            <a class="logo-title" href="/">
               aaton&#39;s study notes
            </a>
        </div>

        <div class="right">
            <div class="pc">
                <ul class="menu-list">
                    
                        <li class="menu-item">
                            <a class=""
                               href="/"
                            >
                                HOME
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/archives"
                            >
                                ARCHIVES
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/tags"
                            >
                                TAGS
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/categories"
                            >
                                CATEGORIES
                            </a>
                        </li>
                    
                        <li class="menu-item">
                            <a class=""
                               href="/about"
                            >
                                ABOUT
                            </a>
                        </li>
                    
                    
                </ul>
            </div>
            <div class="mobile">
                
                <div class="icon-item menu-bar">
                    <div class="menu-bar-middle"></div>
                </div>
            </div>
        </div>
    </div>

    <div class="header-drawer">
        <ul class="drawer-menu-list">
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/">HOME</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/archives">ARCHIVES</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/tags">TAGS</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/categories">CATEGORIES</a>
                </li>
            
                <li class="drawer-menu-item flex-center">
                    <a class=""
                       href="/about">ABOUT</a>
                </li>
            
        </ul>
    </div>

    <div class="window-mask"></div>

</header>


        </div>

        <div class="page-main-content-middle">

            <div class="main-content">

                
                    <div class="fade-in-down-animation">
    <div class="post-page-container">
        <div class="article-content-container">

            <div class="article-title">
                <span class="title-hover-animation">闭包</span>
            </div>

            
                <div class="article-header">
                    <div class="avatar">
                        <img src="/images/avatar.svg">
                    </div>
                    <div class="info">
                        <div class="author">
                            <span class="name">aaton</span>
                            
                                <span class="author-label">Lv3</span>
                            
                        </div>
                        <div class="meta-info">
                            
<div class="article-meta-info">
    <span class="article-date article-meta-item">
        
            <i class="fa-regular fa-calendar-plus"></i>&nbsp;
        
        <span class="pc">2023-12-06 17:18:28</span>
        <span class="mobile">2023-12-06 17:18</span>
    </span>
    
        <span class="article-update-date article-meta-item">
        <i class="fas fa-file-pen"></i>&nbsp;
        <span class="pc">2023-12-08 17:18:30</span>
    </span>
    
    
    
        <span class="article-tags article-meta-item">
            <i class="fas fa-tags"></i>&nbsp;
            <ul>
                
                    <li>
                        <a href="/tags/%E9%97%AD%E5%8C%85/">闭包</a>&nbsp;
                    </li>
                
                    <li>
                        | <a href="/tags/%E9%98%B2%E6%8A%96/">防抖</a>&nbsp;
                    </li>
                
                    <li>
                        | <a href="/tags/%E8%8A%82%E6%B5%81/">节流</a>&nbsp;
                    </li>
                
            </ul>
        </span>
    

    
    
        <span class="article-wordcount article-meta-item">
            <i class="fas fa-file-word"></i>&nbsp;<span>5.6k Words</span>
        </span>
    
    
        <span class="article-min2read article-meta-item">
            <i class="fas fa-clock"></i>&nbsp;<span>22 Mins</span>
        </span>
    
    
        <span class="article-pv article-meta-item">
            <i class="fas fa-eye"></i>&nbsp;<span id="busuanzi_value_page_pv"></span>
        </span>
    
</div>

                        </div>
                    </div>
                </div>
            

            <div class="article-content keep-markdown-body">
                

                <p><img src="https://img.peapix.com/cab3082a49544d289e44b841981b37d5_480.jpg" alt="bing.com-奥林匹克国家公园霍河雨林中的苔藓大厅 ，华盛顿州，美国" title="bing.com-奥林匹克国家公园霍河雨林中的苔藓大厅 ，华盛顿州，美国"></p>
<p>闭包的含义及其使用场景。</p>
<span id="more"></span>

<hr>
<h3 id="闭包是什么"><a href="#闭包是什么" class="headerlink" title="闭包是什么"></a>闭包是什么</h3><p>（1）、 A函数里有一个B函数，B函数引用了A函数作用域里的变量，那么B函数就称为一个闭包，通常会把B函数return出去。</p>
<p>这个定义把闭包看成是一个函数。</p>
<p>（2）、 再看《你不知道的JS》一书里的定义：</p>
<p>一个函数能记住并访问所在的词法作用域时，就形成了闭包，即使函数是在当前词法作用域之外执行。（注：词法作用域是定义在词法阶段的作用域，是由你定义变量、函数的位置决定的）</p>
<p>这个定义把闭包看成是一种现象。</p>
<p>举个例子，可以把闭包看成用保鲜膜将西瓜包起来，然后过段时间，还能吃到新鲜的西瓜。保鲜膜形成的空间，将西瓜的新鲜、水分状态保持住了，让你过段时间还能食用。</p>
<p>闭包是保存变量的状态，以便函数将来“食用”。</p>
<h4 id="理解闭包第一步，理解JS变量的作用域"><a href="#理解闭包第一步，理解JS变量的作用域" class="headerlink" title="理解闭包第一步，理解JS变量的作用域"></a>理解闭包第一步，理解JS变量的作用域</h4><p>要真正理解闭包，首先，我们必须理解Javascript特殊的变量作用域（具体可以参考<a class="link"   target="_blank" rel="noopener" href="https://juejin.cn/post/7096818495450513445" >深入理解JS作用域和作用域链<i class="fas fa-external-link-alt"></i></a>），JavaScript的一个特殊之处就在于它的变量访问规则：</p>
<h5 id="1-函数内部可以直接读取全局变量"><a href="#1-函数内部可以直接读取全局变量" class="headerlink" title="1.函数内部可以直接读取全局变量"></a>1.函数内部可以直接读取全局变量</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">　　var n=999;</span><br><span class="line"></span><br><span class="line">　　function f1()&#123;</span><br><span class="line">　　　　alert(n);</span><br><span class="line">　　&#125;</span><br><span class="line"></span><br><span class="line">　　f1(); // 999</span><br></pre></td></tr></table></figure>

<h5 id="2-在函数外部无法读取函数内的局部变量"><a href="#2-在函数外部无法读取函数内的局部变量" class="headerlink" title="2.在函数外部无法读取函数内的局部变量"></a>2.在函数外部无法读取函数内的局部变量</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">　　function f1()&#123;</span><br><span class="line">　　　　var n=999;</span><br><span class="line">　　&#125;</span><br><span class="line"></span><br><span class="line">　　alert(n);               // Uncaught ReferenceError: n is not defined</span><br></pre></td></tr></table></figure>

<p>但是这里有一个地方需要注意，函数内部声明变量的时候，一定要使用var命令。如果不用的话，你实际上声明了一个全局变量！ （补充：在函数内部声明全局变量被称为隐式声明）</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">　　function f1()&#123;</span><br><span class="line">　　　　n=999;</span><br><span class="line">　　&#125;</span><br><span class="line">　　f1();</span><br><span class="line">　　alert(n); // 999</span><br></pre></td></tr></table></figure>

<h5 id="3-内部函数可以访问外部函数的变量"><a href="#3-内部函数可以访问外部函数的变量" class="headerlink" title="3.内部函数可以访问外部函数的变量"></a>3.内部函数可以访问外部函数的变量</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">　　function f1()&#123;</span><br><span class="line">　　　　var n=999;</span><br><span class="line">    (function f2()&#123;</span><br><span class="line">        alert(n);</span><br><span class="line">    &#125;)();</span><br><span class="line">　　&#125;</span><br><span class="line">f1();//999</span><br></pre></td></tr></table></figure>

<h4 id="理解闭包第二步，如何实现从外部读取局部变量"><a href="#理解闭包第二步，如何实现从外部读取局部变量" class="headerlink" title="理解闭包第二步，如何实现从外部读取局部变量"></a>理解闭包第二步，如何实现从外部读取局部变量</h4><p>在使用JS进行编程的时候，我们有时候需要在函数外部得到函数内的局部变量，可是我们前面已经说了，正常情况下这是不可能的。嘿嘿，既然都说了是正常情况，那肯定有“不正常情况”啦！那就是在函数的内部，再定义一个函数。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">　　function fun1()&#123;</span><br><span class="line">　　　　var n=999;</span><br><span class="line">　　　　function fun2()&#123;</span><br><span class="line">　　　　　　alert(n); // 999</span><br><span class="line">　　　　&#125;</span><br><span class="line">　　&#125;</span><br><span class="line">fun1();</span><br></pre></td></tr></table></figure>

<p>在上面的代码中，函数fun2就被包括在函数fun1内部，这时fun1内部的所有局部变量，对fun2都是可见的。但是反过来就不行，fun2内部的局部变量，对fun1都是不可见的。这就是Javascript语言所特有的链式作用域结构（chain scope），子对象会一级一级地向上寻找所有父对象的变量。总结：父对象的所有变量，对子对象都是可见的，反之则不成立。<br>这样我们就能够访问到函数fun1内部的变量了，可是问题来了，我们无法从外部调用函数fun2，那我们应该怎样才能够在外部访问fun2呢，我们只需要将fun2作为fun1的返回值返回，这样我们不就能在fun1外部访问到fun1内部的变量了吗，如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">　　function fun1()&#123;</span><br><span class="line">　　　　var n=999;</span><br><span class="line">　　　　function fun2()&#123;</span><br><span class="line">　　　　　　alert(n);</span><br><span class="line">　　　　&#125;</span><br><span class="line">　　　　return fun2;</span><br><span class="line">　　&#125;</span><br><span class="line">　　var result=fun1();</span><br><span class="line">　　result(); // 999</span><br></pre></td></tr></table></figure>

<h4 id="理解闭包第三步，闭包的概念"><a href="#理解闭包第三步，闭包的概念" class="headerlink" title="理解闭包第三步，闭包的概念"></a>理解闭包第三步，闭包的概念</h4><p>关于闭包的概念，在前面我们已经说了官方给出的定义，官方给的定义虽然比较准确一点，但是对于初学者来说晦涩难懂，这里来说说我对闭包的理解,我的理解是，闭包就是能够读取其他函数内部变量的函数。在本质上，闭包就是将函数内部和函数外部连接起来的一座桥梁。<br>前面我们虽然也说了闭包不是一个函数，但是为了方便理解和学习我们通常可以将闭包称为一个函数，但是我们也要时刻在心里记住闭包不是一个函数，而是函数和声明该函数的词法环境的组合，这个环境包含了这个闭包创建时所能访问的所有局部变量。记住，它是一个组合！组合！</p>
<h4 id="理解闭包的第四步，闭包的用途"><a href="#理解闭包的第四步，闭包的用途" class="headerlink" title="理解闭包的第四步，闭包的用途"></a>理解闭包的第四步，闭包的用途</h4><p>想要使用闭包，必须知道它的结构，也是它的产生条件：</p>
<ul>
<li><p>一个函数，里面有一些变量和另一个函数。</p>
</li>
<li><p>外部函数里面的函数使用了外部函数的变量。</p>
</li>
<li><p>外部函数最后把它里面的那个函数用return抛出去。</p>
</li>
</ul>
<p>以及闭包的作用：</p>
<ul>
<li><p>在函数外部可以读取函数内部的变量。</p>
</li>
<li><p>让这些变量的值始终保持在内存中。</p>
</li>
</ul>
<p>现在我们再来分析分析文章开始给出的那个题目，为了方便观察，我在这里再插入那段代码：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">var n = 1;</span><br><span class="line">function fun1() &#123;</span><br><span class="line">   test = 10;　　　　</span><br><span class="line">   var n = 999;　　　　</span><br><span class="line">   nAdd = function() &#123;</span><br><span class="line">         n += 1;</span><br><span class="line">         console.log(n);</span><br><span class="line">   &#125;　　　　</span><br><span class="line">   function fun2() &#123;　　　　　　</span><br><span class="line">        console.log(n);　　　　</span><br><span class="line">   &#125;　　　　</span><br><span class="line">   return fun2;　　</span><br><span class="line">&#125;</span><br><span class="line">var result = fun1();</span><br><span class="line">result(); // 999</span><br><span class="line">console.log(test);//10</span><br><span class="line">console.log(n);//1</span><br><span class="line">nAdd();</span><br><span class="line">result(); // 1000</span><br></pre></td></tr></table></figure>

<p>在这段代码中，result实际上就是闭包fun2函数。它一共运行了两次，第一次的值是999，第二次的值是1000。这证明了函数fun1中的局部变量n一直保存在内存中，并没有在fun1调用执行完之后被自动清除。<br>发生这样的情况原因就在于fun1是fun2的父函数，而fun2又通过fun1的return语句被赋给了一个全局变量，这导致fun2始终在内存中，而fun2的存在依赖于fun1，因此fun1也始终在内存中，不会在调用结束后，被垃圾回收机制（garbage collection）回收。<br>另外需要注意的地方是”nAdd&#x3D;function(){n+&#x3D;1}”这一行，首先在nAdd前面没有使用var关键字，因此nAdd是一个全局变量，而不是局部变量。其次，nAdd的值是一个匿名函数，而这个匿名函数本身也是一个闭包，所以nAdd可以在函数外部对函数内部的局部变量进行操作。</p>
<p>使用闭包时需要注意：</p>
<blockquote>
<ul>
<li>由于闭包会使得函数中的变量都被保存在内存中，内存消耗很大，所以不能滥用闭包，否则会造成网页的性能问题，可能导致内存泄露。解决方法是，在退出函数之前，将不需要的局部变量赋值为null。</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>闭包会在父函数外部改变父函数内部变量的值。如果你把父函数当作对象使用，把闭包当作它的公用方法，把内部变量当作它的私有属性，这时一定要注意，不要随便改变父函数内部变量的值。</li>
</ul>
</blockquote>
<blockquote>
<ul>
<li>当定义一个函数时，它实际上保存一个作用域链。当调用这个函数时，它创建一个新的对象来存储它的局部变量，并将这个对象添加至保存的那个作用域链上，同时创建一个新的更长的表示函数调用作用域的“链”。对于嵌套函数来讲，事情变得更加有趣，每次调用外部函数时，内部函数又会重新定义一遍。因为每次调用外部函数的时候，作用域链都是不同的。内部函数在每次定义的时候都有微妙的差别——在每次调用外部函数时，内部函数的代码都是相同的，但是关联这段代码的作用域链不相同，用闭包概念来说，也就是产生了新的闭包。</li>
</ul>
</blockquote>
<p>闭包虽然难，可是闭包真的很重要，非常值得学习，虽然闭包在开发的时候用的很少，但是用到的时候几乎都是不可被别的方式替代的，但凡遇到永久 ，保护等关键字就是用闭包。</p>
<h3 id="词法作用域"><a href="#词法作用域" class="headerlink" title="词法作用域"></a>词法作用域</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">function init() &#123;</span><br><span class="line">  var name = &quot;Mozilla&quot;;         // name 是一个被 init 创建的局部变量</span><br><span class="line">  function displayName() &#123;      // displayName() 是内部函数，一个闭包</span><br><span class="line">    alert(name);                // 使用了父函数中声明的变量</span><br><span class="line">  &#125;</span><br><span class="line">  displayName();</span><br><span class="line">&#125;</span><br><span class="line">init();</span><br></pre></td></tr></table></figure>

<p>init() 创建了一个局部变量 name 和一个名为 displayName() 的函数。displayName() 是定义在 init() 里的内部函数，并且仅在 init() 函数体内可用。请注意，displayName() 没有自己的局部变量。然而，因为它可以访问到外部函数的变量，所以 displayName() 可以使用父函数 init() 中声明的变量 name 。</p>
<p>运行该代码后发现， displayName() 函数内的 alert() 语句成功显示出了变量 name 的值（该变量在其父函数中声明）。这个词法作用域的例子描述了分析器如何在函数嵌套的情况下解析变量名。词法（lexical）一词指的是，<strong>词法作用域根据源代码中声明变量的位置来确定该变量在何处可用</strong>。嵌套函数可访问声明于它们外部作用域的变量。</p>
<h3 id="示例"><a href="#示例" class="headerlink" title="示例"></a>示例</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">function makeFunc() &#123;</span><br><span class="line">  var name = &quot;Mozilla&quot;;</span><br><span class="line">  function displayName() &#123;</span><br><span class="line">    alert(name);</span><br><span class="line">  &#125;</span><br><span class="line">  return displayName;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">var myFunc = makeFunc();</span><br><span class="line">myFunc();</span><br></pre></td></tr></table></figure>

<p>运行这段代码的效果和之前 init() 函数的示例完全一样。其中不同的地方在于内部函数 displayName() 在执行前，从外部函数返回。</p>
<p>第一眼看上去，也许不能直观地看出这段代码能够正常运行。在一些编程语言中，一个函数中的局部变量仅存在于此函数的执行期间。一旦 makeFunc() 执行完毕，你可能会认为 name 变量将不能再被访问。然而，因为代码仍按预期运行，所以在 JavaScript 中情况显然与此不同。</p>
<p>原因在于，JavaScript 中的函数会形成了闭包。 <strong>闭包是由函数以及声明该函数的词法环境组合而成的</strong>。该环境包含了这个闭包创建时作用域内的任何局部变量。在本例子中，myFunc 是执行 makeFunc 时创建的 displayName 函数实例的引用。displayName 的实例维持了一个对它的词法环境（变量 name 存在于其中）的引用。因此，当 myFunc 被调用时，变量 name 仍然可用，其值 Mozilla 就被传递到alert中。</p>
<p>下面是一个更有意思的示例 — 一个 makeAdder 函数：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">function makeAdder(x) &#123;</span><br><span class="line">  return function (y) &#123;</span><br><span class="line">    return x + y;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">var add5 = makeAdder(5);</span><br><span class="line">var add10 = makeAdder(10);</span><br><span class="line"></span><br><span class="line">console.log(add5(2));           // 7</span><br><span class="line">console.log(add10(2));          // 12</span><br></pre></td></tr></table></figure>

<p>在这个示例中，我们定义了 makeAdder(x) 函数，它接受一个参数 x ，并返回一个新的函数。返回的函数接受一个参数 y，并返回x+y的值。</p>
<p>从本质上讲，makeAdder 是一个函数工厂 — 他创建了将指定的值和它的参数相加求和的函数。在上面的示例中，我们使用函数工厂创建了两个新函数 — 一个将其参数和 5 求和，另一个和 10 求和。</p>
<p>add5 和 add10 都是闭包。它们共享相同的函数定义，但是保存了不同的词法环境。在 add5 的环境中，x 为 5。而在 add10 中，x 则为 10。</p>
<h3 id="实例"><a href="#实例" class="headerlink" title="实例"></a>实例</h3><p>闭包很有用，因为它允许将函数与其所操作的某些数据（环境）关联起来。这显然类似于面向对象编程。在面向对象编程中，对象允许我们将某些数据（对象的属性）与一个或者多个方法相关联。</p>
<p>因此，通常你使用只有一个方法的对象的地方，都可以使用闭包。</p>
<p>在 Web 中，你想要这样做的情况特别常见。大部分我们所写的 JavaScript 代码都是基于事件的 — 定义某种行为，然后将其添加到用户触发的事件之上（比如点击或者按键）。我们的代码通常作为回调：为响应事件而执行的函数。</p>
<p>假如，我们想在页面上添加一些可以调整字号的按钮。一种方法是以像素为单位指定 body 元素的 font-size，然后通过相对的 em 单位设置页面中其他元素（例如header）的字号：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">body &#123;</span><br><span class="line">  font-family: Helvetica, Arial, sans-serif;</span><br><span class="line">  font-size: 12px;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">h1 &#123;</span><br><span class="line">  font-size: 1.5em;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">h2 &#123;</span><br><span class="line">  font-size: 1.2em;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>我们的文本尺寸调整按钮可以修改 body 元素的 font-size 属性，由于我们使用相对单位，页面中的其他元素也会相应地调整。</p>
<p>以下是 JavaScript：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">function makeSizer(size) &#123;</span><br><span class="line">  return function () &#123;</span><br><span class="line">    document.body.style.fontSize = size + &quot;px&quot;;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">var size12 = makeSizer(12);</span><br><span class="line">var size14 = makeSizer(14);</span><br><span class="line">var size16 = makeSizer(16);</span><br></pre></td></tr></table></figure>

<p>size12，size14 和 size16 三个函数将分别把 body 文本调整为 12，14，16 像素。我们可以将它们分别添加到按钮的点击事件上。如下所示：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">document.getElementById(&quot;size-12&quot;).onclick = size12;</span><br><span class="line">document.getElementById(&quot;size-14&quot;).onclick = size14;</span><br><span class="line">document.getElementById(&quot;size-16&quot;).onclick = size16;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">&lt;a href=&quot;#&quot; id=&quot;size-12&quot;&gt;12&lt;/a&gt;</span><br><span class="line">&lt;a href=&quot;#&quot; id=&quot;size-14&quot;&gt;14&lt;/a&gt;</span><br><span class="line">&lt;a href=&quot;#&quot; id=&quot;size-16&quot;&gt;16&lt;/a&gt;</span><br></pre></td></tr></table></figure>

<h3 id="模拟私有方法"><a href="#模拟私有方法" class="headerlink" title="模拟私有方法"></a>模拟私有方法</h3><p>编程语言中，比如 Java，是支持将方法声明为私有的，即它们只能被同一个类中的其他方法所调用。</p>
<p>而 JavaScript 没有这种原生支持，但我们可以使用闭包来模拟私有方法。<strong>私有方法不仅仅有利于限制对代码的访问：还提供了管理全局命名空间的强大能力，避免非核心的方法弄乱了代码的公共接口部分</strong>。</p>
<p>下面的示例展现了如何使用闭包来定义公共函数，并令其可以访问私有函数和变量。这个方式也称为模块模式（module pattern）。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">var Counter = (function () &#123;</span><br><span class="line">  var privateCounter = 0;</span><br><span class="line">  function changeBy(val) &#123;</span><br><span class="line">    privateCounter += val;</span><br><span class="line">  &#125;</span><br><span class="line">  return &#123;</span><br><span class="line">    increment: function () &#123;</span><br><span class="line">      changeBy(1);</span><br><span class="line">    &#125;,</span><br><span class="line">    decrement: function () &#123;</span><br><span class="line">      changeBy(-1);</span><br><span class="line">    &#125;,</span><br><span class="line">    value: function () &#123;</span><br><span class="line">      return privateCounter;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;)();</span><br><span class="line"></span><br><span class="line">console.log(Counter.value());           /* logs 0 */</span><br><span class="line">Counter.increment();</span><br><span class="line">Counter.increment();</span><br><span class="line">console.log(Counter.value());           /* logs 2 */</span><br><span class="line">Counter.decrement();</span><br><span class="line">console.log(Counter.value());           /* logs 1 */</span><br></pre></td></tr></table></figure>

<p>在之前的示例中，每个闭包都有它自己的词法环境；而这次我们只创建了一个词法环境，为三个函数所共享：Counter.increment，Counter.decrement 和 Counter.value。</p>
<p>该共享环境创建于一个立即执行的匿名函数体内。<strong>这个环境中包含两个私有项：名为 privateCounter 的变量和名为 changeBy 的函数。这两项都无法在这个匿名函数外部直接访问。必须通过匿名函数返回的三个公共函数访问</strong>。</p>
<p>这三个公共函数是共享同一个环境的闭包。多亏 JavaScript 的词法作用域，它们都可以访问 privateCounter 变量和 changeBy 函数。</p>
<blockquote>
<p>我们定义了一个匿名函数，用于创建一个计数器。我们立即执行了这个匿名函数，并将他的值赋给了变量Counter。我们可以把这个函数储存在另外一个变量makeCounter中，并用他来创建多个计数器。</p>
</blockquote>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line">var makeCounter = function () &#123;</span><br><span class="line">  var privateCounter = 0;</span><br><span class="line">  function changeBy(val) &#123;</span><br><span class="line">    privateCounter += val;</span><br><span class="line">  &#125;</span><br><span class="line">  return &#123;</span><br><span class="line">    increment: function () &#123;</span><br><span class="line">      changeBy(1);</span><br><span class="line">    &#125;,</span><br><span class="line">    decrement: function () &#123;</span><br><span class="line">      changeBy(-1);</span><br><span class="line">    &#125;,</span><br><span class="line">    value: function () &#123;</span><br><span class="line">      return privateCounter;</span><br><span class="line">    &#125;,</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">var Counter1 = makeCounter();</span><br><span class="line">var Counter2 = makeCounter();</span><br><span class="line">console.log(Counter1.value());          /* logs 0 */</span><br><span class="line">Counter1.increment();</span><br><span class="line">Counter1.increment();</span><br><span class="line">console.log(Counter1.value());          /* logs 2 */</span><br><span class="line">Counter1.decrement();</span><br><span class="line">console.log(Counter1.value());          /* logs 1 */</span><br><span class="line">console.log(Counter2.value());          /* logs 0 */</span><br></pre></td></tr></table></figure>

<p>两个计数器 Counter1 和 Counter2 是如何维护它们各自的独立性的。每个闭包都是引用自己词法作用域内的变量 privateCounter 。</p>
<p>每次调用其中一个计数器时，通过改变这个变量的值，会改变这个闭包的词法环境。然而在一个闭包内对变量的修改，不会影响到另外一个闭包中的变量。</p>
<blockquote>
<p>以这种方式使用闭包，提供了许多与面向对象编程相关的好处——特别是数据隐藏和封装。</p>
</blockquote>
<h3 id="错误示例"><a href="#错误示例" class="headerlink" title="错误示例"></a>错误示例</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">&lt;p id=&quot;help&quot;&gt;Helpful notes will appear here&lt;/p&gt;</span><br><span class="line">&lt;p&gt;E-mail: &lt;input type=&quot;text&quot; id=&quot;email&quot; name=&quot;email&quot; /&gt;&lt;/p&gt;</span><br><span class="line">&lt;p&gt;Name: &lt;input type=&quot;text&quot; id=&quot;name&quot; name=&quot;name&quot; /&gt;&lt;/p&gt;</span><br><span class="line">&lt;p&gt;Age: &lt;input type=&quot;text&quot; id=&quot;age&quot; name=&quot;age&quot; /&gt;&lt;/p&gt;</span><br></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">function showHelp(help) &#123;</span><br><span class="line">  document.getElementById(&quot;help&quot;).innerHTML = help;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function setupHelp() &#123;</span><br><span class="line">  var helpText = [</span><br><span class="line">    &#123; id: &quot;email&quot;, help: &quot;Your e-mail address&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;name&quot;, help: &quot;Your full name&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;age&quot;, help: &quot;Your age (you must be over 16)&quot; &#125;,</span><br><span class="line">  ];</span><br><span class="line"></span><br><span class="line">  for (var i = 0; i &lt; helpText.length; i++) &#123;</span><br><span class="line">    var item = helpText[i];</span><br><span class="line">    document.getElementById(item.id).onfocus = function () &#123;</span><br><span class="line">      showHelp(item.help);</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">setupHelp();</span><br></pre></td></tr></table></figure>

<p>数组 helpText 中定义了三个有用的提示信息，每一个都关联于对应的文档中的 input 的 ID。通过循环这三项定义，依次为相应 input 添加了一个 onfocus 事件处理函数，以便显示帮助信息。</p>
<p>运行这段代码后，你会发现它没有达到想要的效果。无论焦点在哪个 input 上，显示的都是关于年龄的信息。</p>
<p>原因是赋值给 onfocus 的是闭包。这些闭包是由他们的函数定义和在 setupHelp 作用域中捕获的环境所组成的。这三个闭包在循环中被创建，但他们共享了同一个词法作用域，在这个作用域中存在一个变量 item。这是因为变量 item 使用 var 进行声明，由于变量提升，所以具有函数作用域。当 onfocus 的回调执行时，item.help 的值被决定。由于循环在事件触发之前早已执行完毕，变量对象 item（被三个闭包所共享）已经指向了 helpText 的最后一项。</p>
<p>解决这个问题的一种方案是使用更多的闭包：特别是使用前面所述的函数工厂：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line">function showHelp(help) &#123;</span><br><span class="line">  document.getElementById(&quot;help&quot;).innerHTML = help;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function makeHelpCallback(help) &#123;</span><br><span class="line">  return function () &#123;</span><br><span class="line">    showHelp(help);</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function setupHelp() &#123;</span><br><span class="line">  var helpText = [</span><br><span class="line">    &#123; id: &quot;email&quot;, help: &quot;Your e-mail address&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;name&quot;, help: &quot;Your full name&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;age&quot;, help: &quot;Your age (you must be over 16)&quot; &#125;,</span><br><span class="line">  ];</span><br><span class="line"></span><br><span class="line">  for (var i = 0; i &lt; helpText.length; i++) &#123;</span><br><span class="line">    var item = helpText[i];</span><br><span class="line">    document.getElementById(item.id).onfocus = makeHelpCallback(item.help);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">setupHelp();</span><br></pre></td></tr></table></figure>

<p>这段代码可以如我们所期望的那样工作。所有的回调不再共享同一个环境， makeHelpCallback 函数为每一个回调创建一个新的词法环境。在这些环境中，help 指向 helpText 数组中对应的字符串。</p>
<p>另一种方法使用了匿名闭包：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">function showHelp(help) &#123;</span><br><span class="line">  document.getElementById(&quot;help&quot;).innerHTML = help;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function setupHelp() &#123;</span><br><span class="line">  var helpText = [</span><br><span class="line">    &#123; id: &quot;email&quot;, help: &quot;Your e-mail address&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;name&quot;, help: &quot;Your full name&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;age&quot;, help: &quot;Your age (you must be over 16)&quot; &#125;,</span><br><span class="line">  ];</span><br><span class="line"></span><br><span class="line">  for (var i = 0; i &lt; helpText.length; i++) &#123;</span><br><span class="line">    (function () &#123;</span><br><span class="line">      var item = helpText[i];</span><br><span class="line">      document.getElementById(item.id).onfocus = function () &#123;</span><br><span class="line">        showHelp(item.help);</span><br><span class="line">      &#125;;</span><br><span class="line">    &#125;)(); // 马上把当前循环项的 item 与事件回调相关联起来</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">setupHelp();</span><br></pre></td></tr></table></figure>

<p>如果不想使用过多的闭包，你可以用 ES2015 引入的 let 或 const 关键词：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">function showHelp(help) &#123;</span><br><span class="line">  document.getElementById(&quot;help&quot;).textContent = help;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function setupHelp() &#123;</span><br><span class="line">  const helpText = [</span><br><span class="line">    &#123; id: &quot;email&quot;, help: &quot;Your e-mail address&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;name&quot;, help: &quot;Your full name&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;age&quot;, help: &quot;Your age (you must be over 16)&quot; &#125;,</span><br><span class="line">  ];</span><br><span class="line"></span><br><span class="line">  for (let i = 0; i &lt; helpText.length; i++) &#123;</span><br><span class="line">    const item = helpText[i];</span><br><span class="line">    document.getElementById(item.id).onfocus = () =&gt; &#123;</span><br><span class="line">      showHelp(item.help);</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">setupHelp();</span><br></pre></td></tr></table></figure>

<p>这个例子使用 const 而不是 var，因此每个闭包都绑定了块作用域的变量，这意味着不再需要额外的闭包。</p>
<p>另一个可选方案是使用 forEach() 来遍历 helpText 数组并给每一个 input 添加一个监听器，如下所示：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line">function showHelp(help) &#123;</span><br><span class="line">  document.getElementById(&quot;help&quot;).textContent = help;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">function setupHelp() &#123;</span><br><span class="line">  var helpText = [</span><br><span class="line">    &#123; id: &quot;email&quot;, help: &quot;Your e-mail address&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;name&quot;, help: &quot;Your full name&quot; &#125;,</span><br><span class="line">    &#123; id: &quot;age&quot;, help: &quot;Your age (you must be over 16)&quot; &#125;,</span><br><span class="line">  ];</span><br><span class="line"></span><br><span class="line">  helpText.forEach(function (text) &#123;</span><br><span class="line">    document.getElementById(text.id).onfocus = function () &#123;</span><br><span class="line">      showHelp(text.help);</span><br><span class="line">    &#125;;</span><br><span class="line">  &#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">setupHelp();</span><br></pre></td></tr></table></figure>

<h3 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h3><ul>
<li><p>数组常用方法forEach、map这些，回调函数调用map方法的item和index参数，就是闭包。</p>
</li>
<li><p>promise里处理拿到的数据，是闭包。</p>
</li>
<li><p>定时器、事件监听器。</p>
</li>
<li><p>Ajax请求。</p>
</li>
<li><p>ES6前实现模块化。</p>
</li>
<li><p>防抖节流。</p>
</li>
<li><p>结合立即执行函数，解决for循环时没有块级作用域问题。</p>
</li>
</ul>
<p><strong>JS中只要是用到了回调函数，那么基本就是闭包了</strong>。</p>
<h3 id="意义"><a href="#意义" class="headerlink" title="意义"></a>意义</h3><ul>
<li><p>避免全局变量污染。</p>
</li>
<li><p>封装性和复用性更好。</p>
</li>
</ul>
<h3 id="性能"><a href="#性能" class="headerlink" title="性能"></a>性能</h3><p>如果不是某些特定任务需要使用闭包，在其他函数中创建函数是不明智的，因为闭包在处理速度和内存消耗方面对脚本性能具有负面影响。</p>
<p>例如，在创建新的对象或者类时，方法通常应该关联于对象的原型，而不是定义到对象的构造器中。原因是这将导致每次构造器被调用时，方法都会被重新赋值一次（也就是说，对于每个对象的创建，方法都会被重新赋值）。</p>
<p>考虑以下示例：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">function MyObject(name, message) &#123;</span><br><span class="line">  this.name = name.toString();</span><br><span class="line">  this.message = message.toString();</span><br><span class="line">  this.getName = function () &#123;</span><br><span class="line">    return this.name;</span><br><span class="line">  &#125;;</span><br><span class="line"></span><br><span class="line">  this.getMessage = function () &#123;</span><br><span class="line">    return this.message;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>在上面的代码中，我们并没有利用到闭包的好处，因此可以避免使用闭包。修改成如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">function MyObject(name, message) &#123;</span><br><span class="line">  this.name = name.toString();</span><br><span class="line">  this.message = message.toString();</span><br><span class="line">&#125;</span><br><span class="line">MyObject.prototype = &#123;</span><br><span class="line">  getName() &#123;</span><br><span class="line">    return this.name;</span><br><span class="line">  &#125;,</span><br><span class="line">  getMessage() &#123;</span><br><span class="line">    return this.message;</span><br><span class="line">  &#125;,</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>但我们不建议重新定义原型。可改成如下例子：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">function MyObject(name, message) &#123;</span><br><span class="line">  this.name = name.toString();</span><br><span class="line">  this.message = message.toString();</span><br><span class="line">&#125;</span><br><span class="line">MyObject.prototype.getName = function () &#123;</span><br><span class="line">  return this.name;</span><br><span class="line">&#125;;</span><br><span class="line">MyObject.prototype.getMessage = function () &#123;</span><br><span class="line">  return this.message;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure>

<p>在前面的两个示例中，继承的原型可以为所有对象共享，不必在每一次创建对象时定义方法。</p>
<hr>
<p><em><strong>参考文献</strong></em><br><a class="link"   target="_blank" rel="noopener" href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures" >MDN - 闭包<i class="fas fa-external-link-alt"></i></a><br><a class="link"   target="_blank" rel="noopener" href="https://juejin.cn/post/7097141521102667813" >稀土掘金 - 深入理解JS闭包<i class="fas fa-external-link-alt"></i></a><br><a class="link"   target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/388522338" >知乎 - 深入理解JS闭包（一）<i class="fas fa-external-link-alt"></i></a></p>

            </div>

            

            
                <ul class="post-tags-box">
                    
                        <li class="tag-item">
                            <a href="/tags/%E9%97%AD%E5%8C%85/">#闭包</a>&nbsp;
                        </li>
                    
                        <li class="tag-item">
                            <a href="/tags/%E9%98%B2%E6%8A%96/">#防抖</a>&nbsp;
                        </li>
                    
                        <li class="tag-item">
                            <a href="/tags/%E8%8A%82%E6%B5%81/">#节流</a>&nbsp;
                        </li>
                    
                </ul>
            

            
                <div class="article-nav">
                    
                        <div class="article-prev">
                            <a class="prev"
                               rel="prev"
                               href="/2023/12/06/JavaScript%E5%B8%B8%E7%94%A8%E5%85%AC%E5%85%B1%E6%96%B9%E6%B3%95%E4%B9%8B%E5%88%A4%E6%96%AD%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E6%8B%AC%E5%8F%B7%E6%98%AF%E5%90%A6%E6%88%90%E5%AF%B9%E5%87%BA%E7%8E%B0/"
                            >
                            <span class="left arrow-icon flex-center">
                              <i class="fas fa-chevron-left"></i>
                            </span>
                                <span class="title flex-center">
                                <span class="post-nav-title-item">JavaScript常用公共方法之判断字符串中括号是否成对出现</span>
                                <span class="post-nav-item">Prev posts</span>
                            </span>
                            </a>
                        </div>
                    
                    
                        <div class="article-next">
                            <a class="next"
                               rel="next"
                               href="/2023/12/06/http%E5%B8%B8%E8%A7%81%E5%93%8D%E5%BA%94%E7%8A%B6%E6%80%81%E7%A0%81/"
                            >
                            <span class="title flex-center">
                                <span class="post-nav-title-item">http常见相应状态码及其含义</span>
                                <span class="post-nav-item">Next posts</span>
                            </span>
                                <span class="right arrow-icon flex-center">
                              <i class="fas fa-chevron-right"></i>
                            </span>
                            </a>
                        </div>
                    
                </div>
            

            
        </div>

        
    </div>
</div>


                
            </div>

        </div>

        <div class="page-main-content-bottom">
            
<footer class="footer">
    <div class="info-container">
        <div class="copyright-info info-item">
            &copy;
            
                <span>2020</span> -
            
            2024
            
                &nbsp;<i class="fas fa-heart icon-animate"></i>
                &nbsp;<a href="/">aaton</a>
            
        </div>
        
            <script async 
                    src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>
            <div class="website-count info-item">
                
                    Visitor Count&nbsp;<span id="busuanzi_value_site_uv"></span>&ensp;
                
                
                    Totalview&nbsp;<span id="busuanzi_value_site_pv"></span>
                
            </div>
        
        <div class="theme-info info-item">
            Powered by <a target="_blank" href="https://hexo.io">Hexo</a>&nbsp;|&nbsp;Theme&nbsp;<a class="theme-version" target="_blank" href="https://github.com/XPoet/hexo-theme-keep">Keep v3.6.1</a>
        </div>
        
        
    </div>
</footer>

        </div>
    </div>

    
        <div class="post-tools">
            <div class="post-tools-container">
    <ul class="tools-list">
        <!-- TOC aside toggle -->
        

        <!-- go comment -->
        
    </ul>
</div>

        </div>
    

    <div class="right-bottom-side-tools">
        <div class="side-tools-container">
    <ul class="side-tools-list">
        <li class="tools-item tool-font-adjust-plus flex-center">
            <i class="fas fa-search-plus"></i>
        </li>

        <li class="tools-item tool-font-adjust-minus flex-center">
            <i class="fas fa-search-minus"></i>
        </li>

        <li class="tools-item tool-dark-light-toggle flex-center">
            <i class="fas fa-moon"></i>
        </li>

        <!-- rss -->
        

        
            <li class="tools-item tool-scroll-to-top flex-center">
                <i class="fas fa-arrow-up"></i>
            </li>
        

        <li class="tools-item tool-scroll-to-bottom flex-center">
            <i class="fas fa-arrow-down"></i>
        </li>
    </ul>

    <ul class="exposed-tools-list">
        <li class="tools-item tool-toggle-show flex-center">
            <i class="fas fa-cog fa-spin"></i>
        </li>
        
    </ul>
</div>

    </div>

    <div class="zoom-in-image-mask">
    <img class="zoom-in-image">
</div>


    

</main>




<script src="/js/utils.js"></script>

<script src="/js/main.js"></script>

<script src="/js/header-shrink.js"></script>

<script src="/js/back2top.js"></script>

<script src="/js/dark-light-toggle.js"></script>







    
<script src="/js/code-block.js"></script>





<div class="post-scripts">
    
        
<script src="/js/post-helper.js"></script>

        
        
    
</div>



</body>
</html>
